﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Kira.Compiler.Bods
{
    public class BodsElementFactory
    {
        public static BodsElementBase Create(XElement xml)
        {
            if (!_hasBuildMapping)
            {
                buildMapping();
                _hasBuildMapping = true;
            }
            Type t;
            try
            {
                t = AttributeValueMapping.GetElementType(xml);
            }
            catch
            {
                try
                {
                    t = AttributeMapping.GetElementType(xml);
                }
                catch
                {
                    try
                    {
                        t = DirectMapping.GetElementType(xml);
                    }
                    catch
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("No Mapping found for ").Append(xml.Name);
                        sb.Append("<").Append(xml.Name).Append(" ");
                        foreach (var attr in xml.Attributes())
                        {
                            sb.Append(attr.Name.ToString() + "=\"" + attr.Value + "\" ");
                        }
                        sb.Append(">");
                        throw new Exception(sb.ToString());
                    }
                }
            }
            BodsElementBase bods = (t.GetConstructor(new Type[] { })).Invoke(new object[] { }) as BodsElementBase;
            bods.Xml = xml;
            return bods;
        }

        private static bool _hasBuildMapping = false;

        private static void buildMapping()
        {
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(c => typeof(BodsElementBase).IsAssignableFrom(c)))
            {
                foreach (var mapping in type.GetCustomAttributes<XmlElementMappingAttribute>())
                {
                    switch (mapping.MappingType)
                    {
                        case XmlElementMappingType.Default:
                            {
                                DirectMapping.Create(type.Name, type);
                                break;
                            }
                        case XmlElementMappingType.ByElement:
                            {
                                DirectMapping.Create(mapping.ElementName, type);
                                break;
                            }
                        case XmlElementMappingType.ByAttribute:
                            {
                                AttributeMapping.Create(mapping.ElementName, mapping.AttributeName, type);
                                break;
                            }
                        case XmlElementMappingType.ByAttributeValue:
                            {
                                AttributeValueMapping.Create(mapping.ElementName, mapping.AttributeName, mapping.AttributeValue, type);
                                break;
                            }
                        default:
                            {
                                //below code should never be reached
                                StringBuilder sb = new StringBuilder();
                                sb.AppendFormat("Unaccepted XmlElementMappingAttribute instance, check if the instance was created in reflection way and it's filed MappingType has been set as an value which is not one of {0}, {1}, {2} or {3};"
                                    , XmlElementMappingType.Default, XmlElementMappingType.ByElement, XmlElementMappingType.ByAttribute, XmlElementMappingType.ByAttributeValue);
                                throw new Exception(sb.ToString());
                            }
                    }
                }
            }
        }

    }
}
